﻿using System;
using System.Collections.Generic;
using System.Text;
using PluginCore;
using System.IO;
using PluginCore.Managers;
using System.Windows.Forms;

namespace MUnitGenerator.Actions
{
    class RunTestActionBase : ActionBase
    {
        protected PluginMain main;
        protected Settings settingObject;

        public RunTestActionBase(PluginMain main, Settings settingObject)
        {
            this.main = main;
            this.settingObject = settingObject;
        }

        protected string GetTestsPath()
        {
            return Path.Combine(Path.GetDirectoryName(PluginBase.CurrentProject.ProjectPath), TestPaths.RootName);
        }

        protected string GetBinPath()
        {
            return Path.Combine(GetTestsPath(), "bin");
        }

        private ProcessEndHandler processEndHandler;

        protected void GenerateTestSuteAndRun(List<string> fullClassNames, Boolean debugMode)
        {
            GenerateTestSuteAndRun(fullClassNames, debugMode, null);
        }

        protected void GenerateTestSuteAndRun(List<string> fullClassNames, string methodToRun)
        {
            GenerateTestSuteAndRun(fullClassNames, true, methodToRun);
        }

        private void GenerateTestSuteAndRun(List<string> fullClassNames, Boolean debugMode, string methodToRun)
        {
            string mainSuiteText =
                "package ;\n" +
                "\n" +
                "import massive.munit.TestSuite;\n" +
                "\n" +
                "class MainTestSuite extends TestSuite\n" +
                "{\n" +
                "\tpublic function new()\n" +
                "\t{\n" +
                "\t\tsuper();\n";
            foreach (string fullClassName in fullClassNames)
            {
                mainSuiteText += "\t\tadd(" + fullClassName + ");\n";
            }
            mainSuiteText +=
                "\t}\n" +
            "}";

            string separateMethodRunText = "";
            if (methodToRun != null)
            {
                foreach (string fullClassName in fullClassNames)
                {
                    separateMethodRunText +=
                        "\t\tvar classFields = haxe.rtti.Meta.getFields(" + fullClassName + ");\n" +
		                "\t\tfor (field in Reflect.fields(classFields))\n" +
		                "\t\t{\n" +
			            "\t\t\tvar fieldMeta = Reflect.field(classFields, field);\n" +
			            "\t\t\tif (field == \"" + methodToRun + "\")\n" +
			            "\t\t\t{\n" +
				        "\t\t\t\tReflect.setField(fieldMeta, \"TestDebug\", null);\n" +
			            "\t\t\t}\n" +
			            "\t\t\telse\n" +
			            "\t\t\t{\n" +
				        "\t\t\t\tif (Reflect.hasField(fieldMeta, \"TestDebug\"))\n" +
				        "\t\t\t\t{\n" +
                        "\t\t\t\t\tReflect.deleteField(fieldMeta, \"TestDebug\");\n" +
				        "\t\t\t\t}\n" +
			            "\t\t\t}\n" +
                        "\t\t}\n";
                }
            }

            string mainText =
                "package ;\n" +
                "\n" +
                "import massive.munit.TestRunner;\n" +
                "import massive.munit.TestSuite;\n" +
                "import massive.munit.ITestResultClient;\n" +
                "\n" +
                "class MUnitMain extends TestRunner\n" +
                "{\n" +
                "\tpublic function new(resultClient:ITestResultClient)\n" +
                "\t{\n" +
                "\t\tsuper(resultClient);\n" +
                "\t\tisDebug = false;\n" +
                "\t}\n" +
                "\t\n" +
                "\tstatic function main()\n" +
                "\t{\n" +
                separateMethodRunText +
                "\t\tvar runner = new MUnitMain(new " + settingObject.ReportClientClass + "());\n" +
                "\t\tvar suites:Array<Class<TestSuite>> = [];\n" +
                "\t\tsuites.push(MainTestSuite);\n" +
                (debugMode ? "\t\trunner.debug(suites);\n" : "\t\trunner.run(suites);\n") +
                "\t}\n" +
                "}";

            string hxmlGeneratedText =
                "-cp " + TestPaths.RootName + "/" + TestPaths.SrcName + "\n" +
                "-cp src\n" +
                "-swf9 " + TestPaths.RootName + "/bin/tests.swf \n" +
                "-swf-header 640:480:30:FFFFFF \n" +
                "--flash-strict\n" +
                "-debug\n" +
                "-D fdb\n" +
                "-main MUnitMain\n" +
                "-lib munit\n";

            string testsPath = GetTestsPath();
            string testsSrcPath = Path.Combine(GetTestsPath(), TestPaths.SrcName);

            string testsBuildHxmlPath = Path.Combine(testsPath, "tests-build.hxml");
            if (!File.Exists(testsBuildHxmlPath))
            {
                File.WriteAllText(testsBuildHxmlPath, "");
            }
            string hxmlText = File.ReadAllText(testsBuildHxmlPath);
            hxmlGeneratedText += hxmlText;

            File.WriteAllText(Path.Combine(testsSrcPath, "MUnitMain.hx"), mainText);
            File.WriteAllText(Path.Combine(testsSrcPath, "MainTestSuite.hx"), mainSuiteText);
            File.WriteAllText(Path.Combine(testsPath, "tests-build-generated.hxml"), hxmlGeneratedText);

            PluginBase.MainForm.CallCommand("SaveAllModified", null);

            string binDirectory = GetBinPath();
            if (!Directory.Exists(binDirectory))
            {
                Directory.CreateDirectory(binDirectory);
            }
            try
            {
                string workDir = PluginBase.MainForm.WorkingDirectory;
                PluginBase.MainForm.WorkingDirectory = Path.GetDirectoryName(PluginBase.CurrentProject.ProjectPath);
                processEndHandler = new ProcessEndHandler(onProcessEnd);
                main.processEndEvent += processEndHandler;
                PluginBase.MainForm.CallCommand(
                    "RunProcessCaptured",
                    Path.Combine(settingObject.HaXePath, "haxe.exe") + ";" +
                    Path.Combine(testsPath, "tests-build-generated.hxml")
                );
                PluginBase.MainForm.WorkingDirectory = workDir;
            }
            catch (Exception exception)
            {
                ErrorManager.ShowError(exception);
            }
        }

        private void onProcessEnd(string result)
        {
            main.processEndEvent -= processEndHandler;

            try
            {
                string binDirectory = GetBinPath();
                string swfPath = Path.Combine(binDirectory, "tests.swf");
                if (File.Exists(swfPath))
                {
                    System.IO.FileInfo info = new System.IO.FileInfo(swfPath);
                    string path = info.Directory.FullName;
                    string trustFile = "FlashDevelop.cfg";
                    ASCompletion.Commands.CreateTrustFile.Run(trustFile, path);

                    DataEvent dePlay = new DataEvent(EventType.Command, "PlaySWF", swfPath);
                    EventManager.DispatchEvent(main, dePlay);
                    if (!dePlay.Handled)
                    {
                        var de = new DataEvent(EventType.Command, "AS3Context.StartDebugger", null);
                        EventManager.DispatchEvent(main, de);

                        System.Diagnostics.Process.Start(swfPath);
                    }
                }
            }
            catch (Exception exception)
            {
                ErrorManager.ShowError(exception);
            }
        }

        protected void CreateTestPathsIfNeed()
        {
            string testsPath = GetTestsPath();
            string testsSrcPath = Path.Combine(testsPath, TestPaths.SrcName);
            if (!Directory.Exists(testsSrcPath))
            {
                Directory.CreateDirectory(testsSrcPath);
            }
        }

        protected List<string> FindTestCasesRecursively()
        {
            List<string> fullClassNames = new List<string>();
            FindTestCases(Path.Combine(GetTestsPath(), TestPaths.SrcName), fullClassNames, "");
            return fullClassNames;
        }

        private void FindTestCases(string path, List<string> fullClassNames, string packageName)
        {
            foreach (string fileName in Directory.GetFiles(path))
            {
                if (Path.GetExtension(fileName) == ".hx" && fileName.IndexOf("Test.hx") != -1)
                {
                    string className = Path.GetFileNameWithoutExtension(fileName);
                    fullClassNames.Add(
                        packageName != "" ? packageName + "." + className : className
                    );
                }
            }
            foreach (string directoryName in Directory.GetDirectories(path))
            {
                string subPackageName = Path.GetFileName(directoryName);
                FindTestCases(
                    Path.Combine(path, directoryName),
                    fullClassNames,
                    packageName != "" ? packageName + "." + subPackageName : subPackageName
                );
            }
        }
    }
}
